﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Data;

namespace Gedea.Core.Manager.DataAccess
{
    public class DataCreate : DataCommon
    {
        public void CreateTable<T>()
            where T : new()
        { //TO DO: Verificar que no exista la tabla y crear relaciones entre tablas.
            string tableName = GetTableName<T>();
            string queryParameters = GetQueryParameters<T>();
            string query = string.Format("CREATE TABLE {0} ({1})", tableName, queryParameters);

            Exec(query);
        }

        public void AddConstraint<T, U>(bool isCascade) //TO DO: reemplazar bool por un enum
            where T : new()
            where U : new()
        {
            string query = string.Format("ALTER TABLE {0} ADD FOREIGN KEY ({2}) REFERENCES {1}({2}) {3}",
                GetTableName<T>(),
                GetTableName<U>(),
                GetPropertyEntityId<U>(new U().GetType().GetProperties()).Name,
                isCascade ? "ON UPDATE CASCADE ON DELETE CASCADE" : "ON UPDATE NO ACTION ON DELETE NO ACTION");

            Exec(query);
        }

        private string GetQueryParameters<T>() where T : new()
        {
            BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Static;
            string queryParams = "";
            PropertyInfo[] properties = new T().GetType().GetProperties(flags);

            PropertyInfo propertyEntityId = GetPropertyEntityId<T>(properties);

            queryParams += GetQueryFormat(propertyEntityId) + " IDENTITY (1, 1) PRIMARY KEY";

            foreach (PropertyInfo p in properties.Where(x => x != propertyEntityId))
            {
                queryParams += string.Format(", {0}", GetQueryFormat(p));
            } //TODO: procesar primero los que no son listas y al terminar el resto.

            return queryParams;
        }

        private string GetQueryFormat(PropertyInfo prop)
        {
            string properties;
            SqlDbType sqlType = GetSqlType(prop);

            if(sqlType == SqlDbType.VarChar)
                properties = GetSqlStringFormat(prop, sqlType);
            else
                properties = sqlType.ToString();
            
            //Type columnType = prop.PropertyType; Para que esta esto?

            // TODO: ó requerida
            properties += IsNullable(prop) ? " NULL" : " NOT NULL";

            return string.Format("{0} {1}", prop.Name, properties);
        }

        private SqlDbType GetSqlType(PropertyInfo prop)
        {
            string netType = prop.PropertyType.Name;
            SqlDbType dbType = new SqlDbType();

            if (IsNullable(prop))
            {
                // If it is NULLABLE, then get the underlying type. eg if "Nullable<int>" then this will return just "int"
                netType = prop.PropertyType.GetGenericArguments()[0].Name;
            }

            if (netType == "Boolean")
                dbType = SqlDbType.Bit;
            else if (netType == "Int32")
                dbType = SqlDbType.Int;
            else if (netType == "String")
                dbType = SqlDbType.VarChar;
            else
            {
                throw new NotImplementedException("Elemento no definido");
            }

            return dbType;
        }

        private static bool IsNullable(PropertyInfo prop)
        {
            return prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>);
        }

        private string GetSqlStringFormat(PropertyInfo prop, SqlDbType dbType)
        {
            string strLengthAtt = new System.ComponentModel.DataAnnotations.StringLengthAttribute(0).GetType().Name;

            var res = prop.GetCustomAttributes(false).ToList()
                .Where(x => x.GetType().Name == strLengthAtt).FirstOrDefault();

            int strlength = res == null ? 0 : ((System.ComponentModel.DataAnnotations.StringLengthAttribute)(res)).MaximumLength;

            return string.Format("{0}({1})", dbType.ToString(), strlength);
        }
    }
}
